package com.lframework.xingyun.core.poi;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
import com.lframework.xingyun.core.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
public class EasyExcelUtil {

    public static void exportZip(String zipName, List<ExcelFile> list, HttpServletResponse response) {
        try {
            // 文件名称
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + getFileName2(zipName + ".zip"));

            // response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename="
            //         + new String(zipName.getBytes("UTF-8"), "ISO8859-1") + ".zip");
            response.setContentType("application/vnd.ms-excel");

            // 准备zip输出流
            ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            // 循环导出多个xlsx文件
            for(ExcelFile excelFile : list) {
                // 准备zip对象
                ZipEntry entry = new ZipEntry(excelFile.getFileName() + ".xlsx");
                zos.putNextEntry(entry);

                // 准备填充模板的对象
                ExcelWriter build = EasyExcel.write(byteArrayOutputStream).withTemplate(getInputStream(excelFile.getSheetInfoBeanList())).build();

                // 单个文件完成
                build.finish();

                // 添加到zip输出流中
                zos.write(byteArrayOutputStream.toByteArray());
                byteArrayOutputStream.reset();
            }

            zos.flush();
            zos.close();
        } catch (Exception e) {
            log.error("exportZip error", e);
        }
    }

    public static void exportZipNoModel(String zipName, List<ExcelFile> list, HttpServletResponse response) {
        try {
            // 文件名称
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + getFileName2(zipName + ".zip"));
            response.setContentType("application/vnd.ms-excel");

            // 准备zip输出流
            ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            // 循环导出多个xlsx文件
            for(ExcelFile excelFile : list) {
                // 准备zip对象
                ZipEntry entry = new ZipEntry(excelFile.getFileName() + ".xlsx");
                zos.putNextEntry(entry);

                // 准备填充模板的对象
                ExcelWriter build = EasyExcel.write(byteArrayOutputStream).withTemplate(getInputStreamNoModel(excelFile.getSheetInfoBeanNoModelList())).build();

                // 单个文件完成
                build.finish();

                // 添加到zip输出流中
                zos.write(byteArrayOutputStream.toByteArray());
                byteArrayOutputStream.reset();
            }

            zos.flush();
            zos.close();
        } catch (Exception e) {
            log.error("exportZip error", e);
        }
    }
    private static InputStream getInputStreamNoModel(List<SheetInfoBeanNoModel> list) {
        //创建一个流，等待写入excel文件内容
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        //将excel文件写入byteArrayOutputStream中
        ExcelWriter writer = EasyExcel.write(byteArrayOutputStream).build();
        list.forEach(item -> {
            Assert.isTrue(item.getSheetIndex() >= 0, "sheetIndex不能小于0!");
            WriteSheet writeSheet = EasyExcel.writerSheet(item.getSheetIndex(), item.getSheetName())
                    .head(head(item.getHeadList()))
                    .registerWriteHandler(CellStyleUtils.getHorizontalCellStyleStrategy())
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)25, (short)25))
                    .registerWriteHandler(new SimpleColumnWidthStyleStrategy(10))
                    .build();
            writer.write(item.getDataList(), writeSheet);
        });
        writer.finish();

        //创建inputStream流
        InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return inputStream;
    }

    private static InputStream getInputStream(List<SheetInfoBean> list) {
        //创建一个流，等待写入excel文件内容
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        //将excel文件写入byteArrayOutputStream中
        ExcelWriter writer = EasyExcel.write(byteArrayOutputStream).build();
        list.forEach(item -> {
            Assert.isTrue(item.getSheetIndex() >= 0, "sheetIndex不能小于0!");
            WriteSheet writeSheet = EasyExcel.writerSheet(item.getSheetIndex(), item.getSheetName())
                    .head(item.getHeadClass())
                    .registerWriteHandler(CellStyleUtils.getHorizontalCellStyleStrategy())
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)25, (short)25))
                    .registerWriteHandler(new SimpleColumnWidthStyleStrategy(10))
                    .build();
            writer.write(item.getDataList(), writeSheet);
        });
        writer.finish();

        //创建inputStream流
        InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return inputStream;
    }

    public static <T> void exportExcel(List<T> list, Class<T> clazz, String fileName, HttpServletResponse response) {
        // 设置返回值类型为
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 编码 防止中文乱码
            response.setHeader("Content-disposition", "attachment;filename=" + getFileName(fileName) + ".xlsx");
            EasyExcel.write(response.getOutputStream(), clazz)
                    .excelType(ExcelTypeEnum.XLSX)
                    .registerWriteHandler(CellStyleUtils.getHorizontalCellStyleStrategy())
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)25, (short)25))
                    .registerWriteHandler(new SimpleColumnWidthStyleStrategy(10))
                    .sheet("sheet1")
                    .doWrite(list);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String getFileName(String fileName) {
        try {
            return URLEncoder.encode(fileName + DateUtils.getMinute(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("getFileName失败", e);
            return fileName;
        }
    }

    private static String getFileName2(String fileName) {
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            return fileName + ";" + "filename*=utf-8''" + fileName;
        } catch (UnsupportedEncodingException e) {
            log.error("getFileName失败", e);
            return fileName;
        }
    }

    private static void writeOutputStream(List<SheetInfoBean> list, OutputStream outputStream) {
        ExcelWriter writer = EasyExcel.write(outputStream).build();
        list.forEach(item -> {
            Assert.isTrue(item.getSheetIndex() >= 0, "sheetIndex不能小于0!");
            WriteSheet writeSheet = EasyExcel.writerSheet(item.getSheetIndex(), item.getSheetName())
                    .head(item.getHeadClass())
                    .registerWriteHandler(CellStyleUtils.getHorizontalCellStyleStrategy())
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)25, (short)25))
                    .registerWriteHandler(new SimpleColumnWidthStyleStrategy(10))
                    .build();
            writer.write(item.getDataList(), writeSheet);
        });
        writer.finish();
    }


    /**
     * @param fileName  文件路径名
     * @param list      查询出来的数据
     * @param headList  传入的Excel头（例如：姓名，生日）
     * @param fileList  传入需要展示的字段（例如：姓名对应字段是name,生日对应字段是birthday）
     */
    public static void noModelWrite(List<?> list, String fileName, List<String> headList, List<String> fileList, HttpServletResponse response) {
        // 设置返回值类型为
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 编码 防止中文乱码
            response.setHeader("Content-disposition", "attachment;filename=" + getFileName(fileName) + ".xlsx");
            EasyExcel.write(response.getOutputStream())
                    .head(head(headList))
                    .excelType(ExcelTypeEnum.XLSX)
                    .registerWriteHandler(CellStyleUtils.getHorizontalCellStyleStrategy())
                    .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)25, (short)25))
                    .registerWriteHandler(new SimpleColumnWidthStyleStrategy(10))
                    .sheet("sheet1")
                    .doWrite(dataList(list, fileList));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置Excel头
     *
     * @param headList Excel头信息
     * @return
     */
    private static List<List<String>> head(List<String> headList) {
        List<List<String>> list = new ArrayList<>();
        for (String value : headList) {
            List<String> head = new ArrayList<>();
            head.add(value);
            list.add(head);
        }
        return list;
    }

    /**
     * 设置表格信息
     *
     * @param dataList 查询出的数据
     * @param fileList 需要显示的字段
     * @return
     */
    private static List<List<?>> dataList(List<?> dataList, List<String> fileList) {
        List<List<?>> list = new ArrayList<>();
        for (Object person : dataList) {
            List<Object> data = new ArrayList<>();
            for (String fieldName : fileList) {
                /**通过反射根据需要显示的字段，获取对应的属性值*/
                data.add(getFieldValue(fieldName, person));
            }
            list.add(data);
        }
        return list;
    }

    /**
     * 根据传入的字段获取对应的get方法，如name,对应的getName方法
     *
     * @param fieldName 字段名
     * @param person    对象
     * @return
     */
    private static Object getFieldValue(String fieldName, Object person) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = person.getClass().getMethod(getter);
            return method.invoke(person);
        } catch (Exception e) {
            return null;
        }
    }

    public static MultipartFile fileToMultipartFile(File file, String contentType) {
        if (StringUtils.isBlank(contentType)) {
            contentType = "text/plain";
        }
        FileItem fileItem = createFileItem(file, contentType);
        MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
        return multipartFile;
    }

    private static FileItem createFileItem(File file, String contentType) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        FileItem item = factory.createItem("textField", contentType, true, file.getName());
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        try {
            FileInputStream fis = new FileInputStream(file);
            OutputStream os = item.getOutputStream();
            while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return item;
    }
}


